﻿using System.Drawing;

namespace CanvasControl.Model
{
    /// <summary>
    /// Модель области отображения.
    /// </summary>
    public class ViewPortModel 
    {
        const float MIN_SCALE_FACTOR = 0.1f;
        const float MAX_SCALE_FACTOR = 2.0f;
        const float SCALE_DELTA = 0.1f;

        Rectangle viewportRect;

        public Point Position { get; private set; }
        public float Scale { get; private set; }
        public Size Size { get; private set; }

        public ViewPortModel()
        {
            Scale = 1.0f;
        }

        /// <summary>
        /// Преобразует координаты точки области отображения координаты отображения на экране.
        /// </summary>
        /// <param name="p">Координаты точки.</param>
        public Point GlobalToClient(Point p)
        {
            return new Point((int)(Size.Width / 2.0f + (p.X + Position.X) * Scale), (int)(Size.Height / 2.0 + (p.Y + Position.Y) * Scale));
        }

        /// <summary>
        /// Преобразует координаты точки контрола в координаты области отображения.
        /// </summary>
        /// <param name="p">Координаты точки.</param>
        public Point ClientToGlobal(Point p)
        {
            return new Point((int)((p.X - Size.Width / 2.0f) / Scale - Position.X), (int)((p.Y - Size.Height / 2.0f) / Scale - Position.Y));
        }

        /// <summary>
        /// Преобразует координаты прямоугольника из глобальных координат в координаты области отображения.
        /// </summary>
        /// <param name="rect">Прямоугольник.</param>
        public Rectangle GlobalToClient(Rectangle rect)
        {
            return new Rectangle(GlobalToClient(rect.Location), new Size((int)(rect.Width * Scale), (int)(rect.Height * Scale)));
        }

        /// <summary>
        /// Преобразует координаты прямоугольника из координат области отображения в глобальные координаты.
        /// </summary>
        /// <param name="rect">Прямоугольник.</param>
        public Rectangle ClientToGlobal(Rectangle rect)
        {
            return new Rectangle(ClientToGlobal(rect.Location), new Size((int)(rect.Width / Scale), (int)(rect.Height / Scale)));
        }

        /// <summary>
        /// Увеличивает масштаб отображения.
        /// </summary>
        public void ZoomIn()
        {
            if (Scale >= MAX_SCALE_FACTOR)
                return;
            Scale += SCALE_DELTA;
            UpdateViewRect();
        }

        /// <summary>
        /// Уменьшает масштаб отображения.
        /// </summary>
        public void ZoomOut()
        {
            if (Scale <= MIN_SCALE_FACTOR)
                return;
            Scale -= SCALE_DELTA;
            UpdateViewRect();
        }

        /// <summary>
        /// Изменяет размер области отображения.
        /// </summary>
        public void Resize(Size size)
        {
            Size = size;
            UpdateViewRect();
        }

        /// <summary>
        /// Сдвигает область отображения в глобальных координатах.
        /// </summary>
        public void Move(int dx, int dy)
        {
            Position = new Point(Position.X + (int) (dx/Scale), Position.Y + (int) (dy/Scale));
            UpdateViewRect();
        }

        /// <summary>
        /// Определяет видимость точки <paramref name="p"/> в области отображения.
        /// </summary>
        /// <param name="p">Глобальные координаты точки.</param>
        /// <returns>Возвращает true, если точка видима, иначе false.</returns>
        public bool IsVisible(Point p)
        {
            return viewportRect.Contains(p);
        }

        /// <summary>
        /// Определяет видимость (полную или частичную) прямоугольника в области отображения.
        /// </summary>
        /// <param name="r">Прямоугольник, описывающий область в глобальных координатах.</param>
        /// <returns>Возвращает true, если область видима, иначе false.</returns>
        public bool IsVisible(Rectangle r)
        {
            return viewportRect.IntersectsWith(r);
        }

        void UpdateViewRect()
        {
            viewportRect = ClientToGlobal(new Rectangle(0, 0, Size.Width, Size.Height));
        }
    }
}
